package io.task.cc.message.socket;

import io.task.cc.message.socket.lock.SetWithLock;
import io.task.cc.message.socket.task.DecodeTask;
import io.task.cc.message.socket.task.HandleTask;
import io.task.cc.message.socket.task.SendTask;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.HashSet;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Slf4j
@Setter
@Getter
public abstract class ChannelContext {


    public boolean isReconnect = false;
    /**
     * 解码出现异常时，是否打印异常日志
     * 此值默认与org.tio.core.TioConfig.logWhenDecodeError保持一致
     */
    public boolean logWhenDecodeError = false;
    /**
     * 此值不设时，心跳时间取org.tio.core.TioConfig.heartbeatTimeout
     * 当然这个值如果小于org.tio.core.TioConfig.heartbeatTimeout，定时检查的时间间隔还是以org.tio.core.TioConfig.heartbeatTimeout为准，只是在判断时用此值
     */
    public Long heartbeatTimeout = null;
    /**
     * 一个packet所需要的字节数（用于应用告诉框架，下一次解码所需要的字节长度，省去冗余解码带来的性能损耗）
     */
    public Integer packetNeededLength = null;
    public Config config = null;
    public DecodeTask decodeRunnable = null;
    public HandleTask handlerRunnable = null;
    public SendTask sendRunnable = null;
    public final ReentrantReadWriteLock closeLock = new ReentrantReadWriteLock();
    private ReadCompletionHandler readCompletionHandler = null;                                            //new ReadCompletionHandler(this);
    public WriteCompletionHandler writeCompletionHandler = null;                                            //new WriteCompletionHandler(this);

    public String userid;
    private String token;
    private String bsId;
    public boolean isWaitingClose = false;
    public boolean isClosed = true;
    public boolean isRemoved = false;

    public final ChannelState stat = new ChannelState();
    /**
     * The asynchronous socket channel.
     */
    public AsynchronousSocketChannel asynchronousSocketChannel;
    private String id = null;
    private Node clientNode;
    private Node proxyClientNode = null;                                            //一些连接是代理的，譬如web服务器放在nginx后面，此时需要知道最原始的ip
    private Node serverNode;
    /**
     * 该连接在哪些组中
     */
    private SetWithLock<String> groups = null;
    private Integer readBufferSize = null;                                            //个性化readBufferSize
    public CloseMeta closeMeta = new CloseMeta();
    private CloseCode closeCode = CloseCode.INIT_STATUS;                        //连接关闭的原因码

    public ChannelContext(Config config, AsynchronousSocketChannel asynchronousSocketChannel) {
        super();
        init(config, asynchronousSocketChannel);


    }


    /**
     * 创建Node
     */
    public abstract Node createClientNode(AsynchronousSocketChannel asynchronousSocketChannel) throws IOException;


    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        ChannelContext other = (ChannelContext) obj;
        if (id == null) {
            return other.id == null;
        } else {
            return id.equals(other.id);
        }
    }


    @Override
    public int hashCode() {
        if (id != null) {
            return this.id.hashCode();
        } else {
            return super.hashCode();
        }
    }

    public void init(Config config, AsynchronousSocketChannel asynchronousSocketChannel) {
        id = UUID.randomUUID().toString();
        this.setConfig(config);
        config.ids.bind(this);
        this.setAsynchronousSocketChannel(asynchronousSocketChannel);
        this.readCompletionHandler = new ReadCompletionHandler(this);
        this.writeCompletionHandler = new WriteCompletionHandler(this);
        this.logWhenDecodeError = config.logWhenDecodeError;

        initOther();
    }

    void initOther() {
        //在长连接中，绑定群组几乎是必须要干的事，所以直接在初始化时给它赋值，省得在后面做同步处理
        groups = new SetWithLock<String>(new HashSet<>());
    }


    public void processAfterSent(Packet packet, Boolean isSentSuccess) {
        isSentSuccess = isSentSuccess != null && isSentSuccess;
        PacketMetaData packetMetaData = packet.getPacketMetaData();
        if (packetMetaData != null) {
            CountDownLatch countDownLatch = packetMetaData.getCountDownLatch();
            //			traceBlockPacket(SynPacketAction.BEFORE_DOWN, packet, countDownLatch, null);
            countDownLatch.countDown();
        }

        try {
            if (log.isDebugEnabled()) {
                log.debug("{} 已经发送 {}", this);
            }


        } catch (Throwable e) {
            log.error(e.toString(), e);
        }


    }


    public void setAsynchronousSocketChannel(AsynchronousSocketChannel asynchronousSocketChannel) {
        this.asynchronousSocketChannel = asynchronousSocketChannel;

        try {
            Node clientNode = createClientNode(asynchronousSocketChannel);
            setClientNode(clientNode);
        } catch (IOException e) {
            log.info(e.toString(), e);

        }
    }


    public void setClientNode(Node clientNode) {
        {
            if (this.clientNode != null) {
                config.clientNodes.remove(this);
            }
        }

        this.clientNode = clientNode;


        config.clientNodes.put(this);
    }


    public void setClosed(boolean isClosed) {
        this.isClosed = isClosed;

    }


    public void setConfig(Config config) {
        this.config = config;

        if (config != null) {
            decodeRunnable = new DecodeTask(this, config.tioExecutor);
            handlerRunnable = new HandleTask(this, config.tioExecutor);
            sendRunnable = new SendTask(this, config.tioExecutor);
            config.connections.add(this);
        }
    }


    /**
     * 是否是服务器端
     */
    public abstract boolean isServer();


    public Integer getReadBufferSize() {
        if (readBufferSize != null && readBufferSize > 0) {
            return readBufferSize;
        }
        return this.config.getReadBufferSize();
    }

    public void setReadBufferSize(Integer readBufferSize) {
        this.readBufferSize = Math.min(readBufferSize, Packet.MAX_DATA_LENGTH);
    }


    public static class CloseMeta {
        public Throwable throwable;
        public String remark;
        public boolean isNeedRemove;

        public Throwable getThrowable() {
            return throwable;
        }

        public void setThrowable(Throwable throwable) {
            this.throwable = throwable;
        }

        public String getRemark() {
            return remark;
        }

        public void setRemark(String remark) {
            this.remark = remark;
        }

        public boolean isNeedRemove() {
            return isNeedRemove;
        }

        public void setNeedRemove(boolean isNeedRemove) {
            this.isNeedRemove = isNeedRemove;
        }
    }

    /**
     * 连接关闭码
     */
    public enum CloseCode {
        /**
         * 没有提供原因码
         */
        NO_CODE((byte) 1),
        /**
         * 读异常
         */
        READ_ERROR((byte) 2),
        /**
         * 写异常
         */
        WRITER_ERROR((byte) 3),
        /**
         * 解码异常
         */
        DECODE_ERROR((byte) 4),
        /**
         * 通道未打开
         */
        CHANNEL_NOT_OPEN((byte) 5),
        /**
         * 读到的数据长度是0
         */
        READ_COUNT_IS_ZERO((byte) 6),
        /**
         * 对方关闭了连接
         */
        CLOSED_BY_PEER((byte) 7),
        /**
         * 读到的数据长度小于-1
         */
        READ_COUNT_IS_NEGATIVE((byte) 8),
        /**
         * 写数据长度小于0
         */
        WRITE_COUNT_IS_NEGATIVE((byte) 9),
        /**
         * 心跳超时
         */
        HEARTBEAT_TIMEOUT((byte) 10),
        /**
         * 连接失败
         */
        CLIENT_CONNECTION_FAIL((byte) 80),

        /**
         * SSL握手时发生异常
         */
        SSL_ERROR_ON_HANDSHAKE((byte) 50),
        /**
         * SSL session关闭了
         */
        SSL_SESSION_CLOSED((byte) 51),
        /**
         * SSL加密时发生异常
         */
        SSL_ENCRYPTION_ERROR((byte) 52),
        /**
         * SSL解密时发生异常
         */
        SSL_DECRYPT_ERROR((byte) 53),

        /**
         * 供用户使用
         */
        USER_CODE_0((byte) 100),
        /**
         * 供用户使用
         */
        USER_CODE_1((byte) 101),
        /**
         * 供用户使用
         */
        USER_CODE_2((byte) 102),
        /**
         * 供用户使用
         */
        USER_CODE_3((byte) 103),
        /**
         * 供用户使用
         */
        USER_CODE_4((byte) 104),
        /**
         * 供用户使用
         */
        USER_CODE_5((byte) 105),
        /**
         * 供用户使用
         */
        USER_CODE_6((byte) 106),
        /**
         * 供用户使用
         */
        USER_CODE_7((byte) 107),
        /**
         * 供用户使用
         */
        USER_CODE_8((byte) 108),
        /**
         * 供用户使用
         */
        USER_CODE_9((byte) 109),
        /**
         * 供用户使用
         */
        USER_CODE_10((byte) 110),
        /**
         * 初始值
         */
        INIT_STATUS((byte) 199),
        /**
         * 其它异常
         */
        OTHER_ERROR((byte) 200),
        ;

        Byte value;

        CloseCode(Byte value) {
            this.value = value;
        }

        public Byte getValue() {
            return value;
        }

        public void setValue(Byte value) {
            this.value = value;
        }
    }
}
